﻿using System;
using System.Collections.Generic;
using System.Threading;
#if SILVERLIGHT
using System.Windows.Browser;
#endif

namespace Sonce.Common
{
    public class Logger
    {
        #region Exception queue
        private const int maxCapacity = 25;
        private static Timer queueTimer = null;
        private static TimerCallback callback = null;
        private static Queue<Exception> jsQueue = new Queue<Exception>(maxCapacity);
        private static Queue<Exception> dbQueue = new Queue<Exception>(maxCapacity);
        private static Queue<Exception> wsQueue = new Queue<Exception>(maxCapacity);
        private static Queue<Exception> mailQueue = new Queue<Exception>(maxCapacity);

        public static bool EnableQueue
        {
            get
            {
                return (callback != null && queueTimer != null);
            }
            set
            {
                if (value)
                {
                    callback = new TimerCallback(delegate { processQueueItem(); });
                    queueTimer = new Timer(callback, null, 100, 5000);
                }
                else
                {
                    callback = null;
                    queueTimer = null;
                }
            }
        }

        private static void processQueueItem()
        {
            Exception ex;

            try
            {
                if (jsQueue.Count > 0)
                {
                    ex = jsQueue.Dequeue();
                    WriteException(ex);
                }
            }
            catch
            {
                //Do NOT handle exception, it may cause stack overflow
            }

            try
            {
                if (mailQueue.Count > 0)
                {
                    ex = mailQueue.Dequeue();
                    //WebService.SendMail(ex);
                }
            }
            catch
            {

            }
        }
        #endregion

        #region Static members
        private static int logLevel = (int)LogLevels.Normal;
        private static bool enableLogging = true;
        #endregion

        #region Static properties
        /// <summary>
        /// Gets or sets the upper level that is allowed to log. Values are defined in a LogLevels enum.
        /// </summary>
        public static int LogLevel
        {
            get
            {
                if (logLevel < (int)LogLevels.Crash)
                {
                    logLevel = (int)LogLevels.Crash;
                }
                else if (logLevel > (int)LogLevels.Detailed)
                {
                    logLevel = (int)LogLevels.Detailed;
                }
                return logLevel;
            }
            set
            {
                logLevel = value;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether logging is enabled.
        /// </summary>
        public static bool EnableLogging
        {
            get
            {
                return enableLogging;
            }
            set
            {
                enableLogging = value;
            }
        }
        #endregion

        #region Static methods
        /// <summary>
        /// Overloaded. Log exception.
        /// </summary>
        /// <param name="ex">Exception.</param>
        public static void Log(Exception ex)
        {
            Log(ex, (int)LogLevels.Normal);
        }

        /// <summary>
        /// Overloaded. Log exception.
        /// </summary>
        /// <param name="ex">Exception.</param>
        /// <param name="level">Value from LogLevel enum.</param>
        public static void Log(Exception ex, LogLevels level)
        {
            Log(ex, (int)level);
        }

        /// <summary>
        /// Log exception.
        /// </summary>
        /// <param name="ex">Exception.</param>
        /// <param name="level">Value from LogLevel enum.</param>
        public static void Log(Exception ex, int level)
        {
            //NOTE: Insert breakpoint here to catch exception while debugging
            if (EnableLogging && LogLevel <= level)
            {
                //Possibilities:
                //TODO: Log exception in html/javascript
                //TODO: Insert exception into internal database
                //TODO: Insert exception into external database
                //TODO: Send exception to developer's e-mail
                //TODO: Send excetpion via socket to bug tracking system
            }
        }

        /// <summary>
        /// Print exception message and stack trace to the browser's javascript console.
        /// </summary>
        /// <param name="ex">Exception.</param>
        public static void WriteException(Exception ex)
        {
            //Deployment.Current.Dispatcher.BeginInvoke(delegate { WriteException(ex); });

            if (EnableLogging)
            {
                try
                {
                    if (HtmlPage.IsEnabled)
                    {
#if SILVERLIGHT
                        string message = ex.Message + ex.StackTrace;
                        message = message.Replace('"', '\'').Replace("\r\n", @"\n");
                        HtmlPage.Window.Eval("throw new Error(\"Unhandled Error in Silverlight 2 Application " + message + "\");");
#else
                        string message = ex.Message + ex.StackTrace;
                        File.WriteAllText(Path.TempFile, message);
#endif
                    }
                }
                catch
                {
                    //Do NOT handle exception here, it may cause stack overflow
                }
            }
        }

        /// <summary>
        /// Print a message to html - call javascript function showMessage(msg).
        /// </summary>
        /// <param name="message">Message to be passed to javascript.</param>
        public static void WriteLine(string message)
        {
            if (EnableLogging)
            {
                try
                {
                    #if SILVERLIGHT
                    message = message.Replace('"', '\'').Replace("\r\n", @"\n");
                    HtmlPage.Window.Eval("showMessage(\"" + message + "\");");
                    #else
                    Console.WriteLine(message);
                    #endif
                }
                catch
                {
                    //Do NOT handle exception here, it may cause stack overflow
                }
            }
        }
        #endregion
    }

    public enum LogLevels
    {
        /// <summary>
        /// Errors that cause application crash.
        /// </summary>
        Crash = 1,
        /// <summary>
        /// Default log level, bugs that need to be fixed.
        /// </summary>
        Normal = 3,
        /// <summary>
        /// Logs every exception and missing files, should be used only when testing.
        /// </summary>
        Detailed = 5,
    }
}
